package cn.bluedot.framework.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

//真正的连接池管理对象
public class PooledDataSourceImpl implements PooledDataSource {
    //准备好连接池所需要的参数
    private static String jdbcDriver = "";
    private static String jdbcurl = "";
    private static String userName = "";
    private static String password = "";
    private static int initCount;
    private static int stepSize;
    private static int poolMaxSize;

    //线程安全
    private static Vector<PooledConnection> poolConnects=new Vector<PooledConnection>();

    public PooledDataSourceImpl(){
        init();
    }
    //初始化这些信息的方法
    private void init(){
        //流的形式
       InputStream is= PooledDataSourceImpl.class.getClassLoader().getResourceAsStream("db.properties");
       //将管道内的字节数据key-value
       Properties properties=new Properties();

        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        jdbcDriver= properties.getProperty("jdbc.driver");
        jdbcurl= properties.getProperty("jdbc.url");
        userName= properties.getProperty("jdbc.username");
        password= properties.getProperty("jdbc.password");
        initCount= Integer.valueOf(properties.getProperty("initCount"));
        stepSize=Integer.valueOf(properties.getProperty("stepSize"));
        poolMaxSize=Integer.valueOf(properties.getProperty("poolMaxSize"));
        //创建数据链 通过反射
        try {
            Driver driver=(Driver)Class.forName(jdbcDriver).newInstance();
            //驱动 注册到大管家
            DriverManager.registerDriver(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //创建数据库连接
        createConnections(initCount);
    }

    /**
     * 任务获取管道方法(消费连接池里面的管道)
     * @return
     */
    @Override
    public PooledConnection getConnection() {
        if(poolConnects.size() <= 0){
            System.out.println("连接池内没有多余的管道对象");
            //通常会打印loger.error(""),这里使用抛出异常
            throw new RuntimeException("连接池内没有多余的管道对象!");
        }
        //进来获取连接管道对象的时候 真是有效的连接管道 判断检测
        PooledConnection pooledConnection = getRealConnection();
        while(pooledConnection == null){
            //证明里面没有空闲的管道 扩容
            createConnections(stepSize);
            getRealConnection();
            //等300毫秒看是否能获取到管道
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return pooledConnection;
    }
    
    //拿一个真实有效的管道 判断是否被占用 检测是否是有效的管道
    private synchronized PooledConnection getRealConnection() {
        //判断连接池里面的管道对象是否是空闲
        for (PooledConnection conn : poolConnects) {
            //
            if (!conn.isBusy()) {
                //这里拿到自定义管道里面的是真管道对象
                Connection connection = conn.getConn();
                try {
                    //isValid底层源码是去数据库查一遍，然后是否有返回 如果有返回代表这个链接管道是有效的
                    if (!connection.isValid(2000)) {
                        //不是有效的，就在这里面创建一个有效的管道
                        Connection validConn = DriverManager.getConnection(jdbcurl, userName, password);
                        conn.setConn(validConn);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn.setBusy(true);
                return conn;
            }
        }
        return null;
    }
    /**
     * 创建数据连接管道是交给连接池去控制
     * @param count
     */
    @Override
    public void createConnections(int count) {
        if(count > 0 && poolConnects.size() + count > poolMaxSize){
            System.out.println("创建连接池管道对象失败,原因是超过上限值");
            throw new RuntimeException("创建连接池管道对象失败,原因是超过上限值!");
        }
        for(int i = 0; i < count; i++) {
            try {
                Connection conn= DriverManager.getConnection(jdbcurl,userName,password);
                //将创建出来的物理管道封装到自定义管道里面 并标识为未被占用
                PooledConnection pooledConnection=new PooledConnection(conn,false);
                //创建好的管道后丢入
                poolConnects.add(pooledConnection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
